package JGameAdapter;


import JGameAdapter.ParticleInterface;
import JGameAdapter.ParticleInterface;

/**
 * In a real project this would be your particle class<br>
 * Important: Your particle class needs to implement the ParticleInterface wich is given by the adapter
 * @author Marvin
 */
public class FakeParticle implements ParticleInterface{

 /** ****************************************************
 *  ToDo: Moin! ich habe die Bremsen falsch gedacht und habe versucht sie umzuschreiben, aber die funktion forceBrake ist fehlerhaft.
 *  Lasse mir die Bremsen morgen von Jan und tim erklären und dann werden wir sehen, wie das ABS und ASR funzt.
 *  des weiteren müssen wir die RangeException wohl rausbekommen, weil wir sonst im JGame Methoden überschreiben würden,
 *  die nicht überschreibbar sind. Dann müssen wir noch die getter und setter implementieren, aber auch das wird fix gemacht sein. sorry dass ich 
 *  nicht weiter gekommen bin
 *  
 * 
 *  Es ist jetzt 20 vor 3. ich gehe jetzt schlafen. cheerio
 * 
 *  PS: JGame ist als Library drin, hoffe das funzt bei dir auch so.
 * 
 * *****************************************************
 */




    // Antriebshebel
    private double level;
    // Zeit in S
    private double time;
    // Position
    private double pos;
    // Geschwindigtkeit in m/s
    private double speed;
    // Bremse
    private double brake;
    
    //Traktionskoeffizient
    private double traction;
    
    // Gewicht in KG
    private static double Mass = 1445.0;
    // Max. Leistung in KW
    private static double PowerPropMax = 456000.0;
    // Max. Speed in m/s
    private static double SpeedMax = 91.6;
    // Min. Speed in m/s
    private static double SpeedMin = 0.0;
    // Erdbeschleunigung in m/(s*s)
    private static double AccEarth = 9.81;
    // max. Kraft
    private double ForcePropMax = Mass * AccEarth;
    
    // aktuelle Kraft
    private double ForcePropMaxCurrent = ForcePropMax;
    
    // max. DragConst
    private static double DragConst = Math.abs(PowerPropMax / Math.pow(SpeedMax, 3));
    // Range für mLevel
    private static double MinLevel = 0.0;
    private static double MaxLevel = 1.0;
    // Umrechnug von m/s nach km/h
    private static double METERPERSECONDSTOKILOMETERPERHOUER = 3.6;

    private boolean abs;
    private boolean asr;
    private boolean abflug;
    private boolean asrActive;
    private boolean absActive;
    private boolean driving;
    
    
    // Der Konstruktor
    public FakeParticle() //throws RangeException
    {  // Variablen auf Default-Werte setzen
        reset();
        
    }
    private boolean checkLevelRange(double Level) //throws RangeException
    {
        // Rückgabe
        boolean bReturn = true;

        // Eingabe im gültigen Bereich?
        if (!((Level <= MaxLevel) && (Level >= MinLevel))) {
            // Nicht aktzeptiert
            bReturn = false;
        }

        // und ab damit
        return bReturn;
    }

    // Setzt alle wichtigen Variablen
    public void set(double Time, double Pos, double Speed, double Level, double Brake) //throws RangeException
    {
        if (checkLevelRange(Level)) {
            this.time = Time;
            this.pos = Pos;
            this.speed = Speed;
            this.level = Level;
            this.brake = Brake;
        } else {
            System.out.println("Impossible level");
        }
    }

    // Setzt alle wichtigen Variablen auf Default-Werte zurück
    public void reset() //throws RangeException
    {
        set(0.0, 0.0, 0.0, 0.0, 0.0);
        abs = true;
        asr = true;
        abflug = false;
        driving = false;
    }

//    // Einen Simulations-Schritt ausführen, wenn nur ein level gegeben wird
    public void step(double DeltaTime, double Level, double BrakeLevel) //throws RangeException
    {
        // Fahrmodus auf Fahren schalten, wenn das Gaspedal getreten wird
        if( Level > 0.0)
        {
            driving = true;
        }
        // Fahrmodus auf Stopp
        else if( speed < 0.01  )
        {
            driving = false;
            speed = 0.0;
        }
        
        // Manipulation nur durchführen, wenn Level gültig ist
        if( checkLevelRange(Level) && checkLevelRange(BrakeLevel) && driving )
        {
            // Regler sind aus
            absActive = false;
            asrActive = false;
            
            // Wie viel % der Leistung
            double powerProp = PowerPropMax * Math.abs(Level);

            // Formeln anwenden
            double forcePropAbs = Math.min(ForcePropMax, powerProp/Math.abs(speed));
            double forceProp = forcePropAbs * Math.signum(Level);
            double forceDrag = DragConst * (speed*speed) * Math.signum(-speed);
            double forceBrake = BrakeLevel * Mass * AccEarth * Math.signum(-speed);
            double force = forceDrag;

            // ABS
            if(Math.abs(forceBrake) > ForcePropMaxCurrent)
            {
                if(!abs)
                {
                    abflug = true;
                }
                else
                {
                    absActive = true;
                    force += ForcePropMaxCurrent * Math.signum(-speed);;
                }
            }
            else
                force += forceBrake;

            // ASR
            if(Math.abs(forceProp) > ForcePropMaxCurrent)
            {
                if(asr == false)
                {
                    abflug = true;
                }
                else
                {
                    asrActive = true;
                    force += ForcePropMaxCurrent;
                }
            }
            else
                force += forceProp;

            // Beschleunigung setzen
            double acc = force / Mass;

            // Interne Werte setzen
            this.level = Level;
            this.brake = BrakeLevel;
            pos = pos + (speed * DeltaTime);
            speed = speed + (acc * DeltaTime);       
        }
            time += DeltaTime;
    }
    
//    public void paint() {
//        setImage("Porsche");
//    }

    // Aktuelle Geschwindigkeit in m/s
    public double getSpeed() {
        return speed;
    }

    public void setSpeed(double Speed) {
        this.speed = Speed;

    }

    public double getSpeedInKmH() {
        return getSpeed() * METERPERSECONDSTOKILOMETERPERHOUER;
    }

    // Aktuelle Position in Metern
    public double getPos() {
        return pos;
    }

    public void setPos(double Pos) {
        this.pos = Pos;
    }
    // Aktuelle Simulationszeit in Sekunden

    public double getTime() {
        return time;
    }

    public void setTime(double Time) {
        this.time = Time;
    }

    public double getLevel() {return level;}

    //public void setLevel(double Level) {
    //    this.level = Level;
    //}

    public double getBrake() {return brake;}

    //public void setBrake(double Brake) {
    //    this.brake = Brake;
    //}
    
    public double getTraction() {return traction;}
    
    public void setTraction(double Traction) {
        this.traction = Traction;
        this.ForcePropMaxCurrent = ForcePropMax * Traction;
    }
    
    public double getForcePropMax() {return ForcePropMax;}
    
    public void setForcePropMax(double forcePropMax){this.ForcePropMax = forcePropMax;}
    
    public boolean getAbs(){return abs;}
    
    public void setAbs(boolean abs) {this.abs = abs;}
    
    public boolean getAsr() {return asr;}
    public void setAsr(boolean asr){this.asr = asr;}

    public boolean absActive() {return absActive;}

    public boolean asrActive(){return asrActive;}

    public boolean abflug(){return abflug;}
}

